import asyncio
import json
import os
import random
from argparse import ArgumentParser

import httpx

# 请求头
headers = {
    "accept": "application/json, text/plain, */*",
    "accept-encoding": "gzip, deflate, br, zstd",
    "accept-language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
    "origin": "https://www.bilibili.com",
    "priority": "u=1, i",
    "referer": "https://www.bilibili.com/",
    "sec-ch-ua": '"Chromium";v="140", "Not=A?Brand";v="24", "Microsoft Edge";v="140"',
    "sec-ch-ua-mobile": "?0",
    "sec-ch-ua-platform": '"Windows"',
    "sec-fetch-dest": "empty",
    "sec-fetch-mode": "cors",
    "sec-fetch-site": "same-site",
    "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36 Edg/140.0.0.0",
}

# API地址
API_URL = "https://api.bilibili.com/x/web-interface/newlist_rank"


def get_fixed_date_ranges() -> list[dict[str, str]]:
    """生成固定的24个月度日期范围，从20230915到20250915"""
    return [
        # {"start": "20230915", "end": "20231015"},
        # {"start": "20231015", "end": "20231115"},
        # {"start": "20231115", "end": "20231215"},
        # {"start": "20231215", "end": "20240115"},
        # {"start": "20240115", "end": "20240215"},
        # {"start": "20240215", "end": "20240315"},
        # {"start": "20240315", "end": "20240415"},
        # {"start": "20240415", "end": "20240515"},
        # {"start": "20240515", "end": "20240615"},
        # {"start": "20240615", "end": "20240715"},
        # {"start": "20240715", "end": "20240815"},
        # {"start": "20240815", "end": "20240915"},
        # {"start": "20240915", "end": "20241015"},
        # {"start": "20241015", "end": "20241115"},
        # {"start": "20241115", "end": "20241215"},
        # {"start": "20241215", "end": "20250115"},
        # {"start": "20250115", "end": "20250215"},
        # {"start": "20250215", "end": "20250315"},
        # {"start": "20250315", "end": "20250415"},
        # {"start": "20250415", "end": "20250515"},
        # {"start": "20250515", "end": "20250615"},
        # {"start": "20250615", "end": "20250715"},
        # {"start": "20250715", "end": "20250815"},
        # {"start": "20250815", "end": "20250915"},
        {"start": "20240901", "end": "20240930"},
        {"start": "20241001", "end": "20241031"},
        {"start": "20241101", "end": "20241130"},
        {"start": "20241201", "end": "20241231"},
        {"start": "20250101", "end": "20250131"},
        {"start": "20250201", "end": "20250228"},
        {"start": "20250301", "end": "20250331"},
        {"start": "20250401", "end": "20250430"},
        {"start": "20250501", "end": "20250531"},
        {"start": "20250601", "end": "20250630"},
        {"start": "20250701", "end": "20250731"},
        {"start": "20250801", "end": "20250831"},
    ]


async def fetch_videos(
    client: httpx.AsyncClient,
    cate_id: int,
    page: int,
    pagesize: int,
    time_from: str,
    time_to: str,
    order: str = "click",
) -> list[dict[str, str]] | None:
    """
    从B站API获取视频列表，并提取BV号和标签

    Args:
        client: httpx异步客户端
        cate_id: 分区ID
        page: 页码
        pagesize: 每页视频数量
        time_from: 开始时间
        time_to: 结束时间
        order: 排序方式

    Returns:
        包含BV号和标签的字典列表，如果请求失败则返回None
    """
    params = {
        "main_ver": "v3",
        "search_type": "video",
        "view_type": "hot_rank",
        "copy_right": -1,
        "new_web_tag": 1,
        "order": order,
        "cate_id": cate_id,
        "page": page,
        "pagesize": pagesize,
        "time_from": time_from,
        "time_to": time_to,
    }

    try:
        response = await client.get(API_URL, params=params, headers=headers)
        response.raise_for_status()

        data = response.json()

        # 检查响应是否成功
        if data.get("code") != 0:
            return None

        # 提取BV号和标签
        videos = []
        result = data.get("data", {}).get("result", [])
        for item in result:
            if "bvid" in item and "tag" in item:
                videos.append({"bvid": item["bvid"], "tag": item["tag"]})

        return videos

    except httpx.HTTPError:
        return None
    except json.JSONDecodeError:
        return None
    except Exception:
        return None


def save_to_file(file_path: str, videos: list[dict[str, str]]) -> None:
    """将视频数据追加保存到文件"""
    with open(file_path, "a", encoding="utf-8") as f:
        for video in videos:
            f.write(f"{video['bvid']}\t\t{video['tag']}\n")


async def fetch_monthly_videos(
    client: httpx.AsyncClient,
    cate_id: int,
    time_from: str,
    time_to: str,
    file_path: str,
    total_videos: int = 600,
    pagesize: int = 100,
    batch_size: int = 100,
) -> list[dict[str, str]]:
    """
    获取一个月内指定数量的视频BV号和标签，并定期保存

    Args:
        client: httpx异步客户端
        cate_id: 分区ID
        time_from: 开始时间
        time_to: 结束时间
        file_path: 保存文件路径
        total_videos: 需要获取的总视频数量
        pagesize: 每页视频数量
        batch_size: 每积累多少个视频保存一次

    Returns:
        收集到的视频数据列表
    """
    videos = []
    page = 1
    remaining = total_videos

    # 尝试不同的排序方式以获取更多视频
    orders = ["click", "scores", "stow", "coin", "dm"]
    current_order_idx = 0
    batch_count = 0  # batch 计数器

    while remaining > 0 and current_order_idx < len(orders):
        current_order = orders[current_order_idx]
        # 计算当前页需要的视频数量
        current_pagesize = min(pagesize, remaining)

        # 获取当前页视频
        current_videos = await fetch_videos(
            client, cate_id, page, current_pagesize, time_from, time_to, current_order
        )

        # 每次请求后都添加随机间隔
        wait_time = random.uniform(0.1, 0.5)
        await asyncio.sleep(wait_time)

        if not current_videos:
            # 如果当前页没有获取到视频，尝试下一种排序方式
            current_order_idx += 1
            page = 1  # 重置页码
            continue

        # 添加到结果列表
        videos.extend(current_videos)
        remaining -= len(current_videos)

        # 每积累到指定数量就保存一次
        if len(videos) >= batch_size:
            batch_count += 1
            print(f"[{time_from} 到 {time_to}] - 完成第 {batch_count} 个batch")
            save_to_file(file_path, videos)
            videos = []  # 清空列表，避免重复保存

        # 如果当前页获取的视频数量小于请求的数量，说明没有更多视频了
        if len(current_videos) < current_pagesize:
            current_order_idx += 1
            page = 1  # 重置页码
        else:
            page += 1

    # 保存剩余的视频
    if videos:
        save_to_file(file_path, videos)

    # 截取到需要的数量（包括已经保存的）
    return videos[:total_videos]


async def run():
    # 解析命令行参数
    parser = ArgumentParser(description="获取B站视频的BV号和标签")
    parser.add_argument("--tid", type=int, default=24, help="分区ID，默认为24")
    parser.add_argument(
        "--start", type=int, default=0, help="开始月份索引(0-11)，默认为0"
    )
    parser.add_argument(
        "--end", type=int, default=1, help="结束月份索引(1-12)，默认为1"
    )
    parser.add_argument(
        "--videos-per-month",
        type=int,
        default=600,
        help="每月获取的视频数量，默认为600",
    )
    parser.add_argument(
        "--pagesize", type=int, default=100, help="每页视频数量，默认为100"
    )
    args = parser.parse_args()

    # 验证月份范围参数
    if not (0 <= args.start < 12):
        raise ValueError("start参数必须在0-11之间")
    if not (1 <= args.end <= 12):
        raise ValueError("end参数必须在1-12之间")
    if args.start >= args.end:
        raise ValueError("start参数必须小于end参数")

    # 配置参数
    cate_id = args.tid
    videos_per_month = args.videos_per_month
    pagesize = args.pagesize

    # 创建data目录（如果不存在）
    os.makedirs("data", exist_ok=True)
    file_path = f"data/{cate_id}_bvids_tags.txt"

    # 获取固定的日期范围并根据参数截取
    all_date_ranges = get_fixed_date_ranges()
    date_ranges = all_date_ranges[args.start : args.end]
    total_months = len(date_ranges)

    # 创建异步客户端
    async with httpx.AsyncClient(timeout=30) as client:
        # 逐个月份获取视频
        for i, date_range in enumerate(date_ranges, 1):
            await fetch_monthly_videos(
                client,
                cate_id,
                date_range["start"],
                date_range["end"],
                file_path,
                videos_per_month,
                pagesize,
            )

            print(
                f"[{date_range['start']} 到 {date_range['end']}] - 月度数据获取完成\n"
            )

            # 在月份之间额外等待更长时间
            if i < total_months:
                wait_time = random.uniform(5, 10)
                await asyncio.sleep(wait_time)


def main():
    asyncio.run(run())


if __name__ == "__main__":
    main()

# 使用样例：
# python get_data.py --tid 24 --start 0 --end 1
# tid: 子分区
# start 和 end，总共有12个时间段，12个月，0 - 12
# 写入文件是追加的，没有去重，因为发现有些视频没有tag，需要处理，统一处理
